package com.sanq.product.cab.interceptors;

import com.sanq.product.cab.annotations.RateLimitAnnotation;
import com.sanq.product.cab.exceptions.BusException;
import com.sanq.product.cab.redis.JedisPoolService;
import com.sanq.product.cab.redis.constants.BusCode;
import com.sanq.product.cab.tools.log.LogUtil;
import com.sanq.product.cab.tools.strings.StringUtil;
import com.sanq.product.cab.tools.web.GlobalUtil;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;

/**
 * com.sanq.product.cab.interceptors.LogInterceptor
 *
 * @author sanq.Yan
 * @date 2020/2/20
 */
public class RateLimitInterceptor implements HandlerInterceptor {

    String evalLua = "local key = 'limit:' .. KEYS[1]\n" +
            "local limit = tonumber(ARGV[1])\n" +
            "local current = tonumber(redis.call('GET', key) or '0')\n" +
            "if current + 1 > limit then\n" +
            "\treturn -1\n" +
            "else\n" +
            "\tredis.call('INCRBY', key,'1')\n" +
            "\tif redis.call('TTL', key) == -1 then \n" +
            "\t\tredis.call('expire', key,ARGV[2])\n" +
            "\tend\n" +
            "\treturn current + 1\n" +
            "end";

    @Resource
    private JedisPoolService jedisPoolService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod hm = (HandlerMethod) handler;
            Method method = hm.getMethod();
            Class<?> declaringClass = method.getDeclaringClass();
            RateLimitAnnotation rateLimitAnnotation = hm.getMethodAnnotation(RateLimitAnnotation.class);

            if (rateLimitAnnotation != null) {

                String key = GlobalUtil.getBrowserIp(request) + ":" +
                        declaringClass.getName() + ":" +
                        method.getName() + ":" +
                        rateLimitAnnotation.key();

                Integer limitCurrent = StringUtil.toInteger(jedisPoolService.eval(evalLua,
                        Collections.singletonList(key),
                        new ArrayList<String>(2) {{
                            add(String.valueOf(rateLimitAnnotation.limit()));
                            add(String.valueOf(rateLimitAnnotation.limitTime()));
                        }}));

                // 得到当前限流次数 如果小于限定次数
                if (limitCurrent != -1 && limitCurrent <= rateLimitAnnotation.limit()) {
                    LogUtil.getInstance(declaringClass).i("-------\t当前方法: {}.{} \t\t\t-------", declaringClass.getName(), method.getName());
                    LogUtil.getInstance(declaringClass).i("-------\t总限流次数: {}\t\t-------", rateLimitAnnotation.limit());
                    LogUtil.getInstance(declaringClass).i("-------\t当前访问次数: {}\t-------", limitCurrent);

                    return true;
                } else
                    throw new BusException("当前已被限流", BusCode.LIMIT_CODE);

            }
            return true;
        }
        throw new BusException("当前已被限流", BusCode.LIMIT_CODE);
    }

}
